import asyncio
import json
import os

import cv2
import matplotlib
import tornado
from matplotlib import image as mpimg, pyplot as plt
from shapely import Polygon, unary_union

from server.datasource.pg import Connection
from nanoid import generate

pg_connection_pool = None


def init_pg_connection_pool(config):
    global pg_connection_pool
    pg_connection_pool = Connection(config).get_pool()


class FindModel(tornado.web.RequestHandler):

    def set_default_headers(self):
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Headers', '*')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Content-type', 'application/json')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers',
                        'Content-Type, Access-Control-Allow-Origin, '
                        'Access-Control-Allow-Headers, '
                        'X-Requested-By, '
                        'Access-Control-Allow-Methods')

    async def get(self):
        page = int(self.get_argument('page', 1))
        pagesize = int(self.get_argument('pagesize', 100))
        with pg_connection_pool.connection() as connection:
            with connection.cursor() as cursor:
                sql = "select * from model limit {} offset {}".format(pagesize, page - 1)
                cursor.execute(
                    sql
                )
                col_names = [desc[0] for desc in cursor.description]
                results = cursor.fetchall()
                results_with_names = [dict(zip(col_names, row)) for row in results]
                self.write({"status": 1, "results": results_with_names})


class FindWorkspaces(tornado.web.RequestHandler):
    def set_default_headers(self):
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Headers', '*')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Content-type', 'application/json')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers',
                        'Content-Type, Access-Control-Allow-Origin, '
                        'Access-Control-Allow-Headers, '
                        'X-Requested-By, '
                        'Access-Control-Allow-Methods')

    async def get(self):
        with pg_connection_pool.connection() as connection:
            with connection.cursor() as cursor:
                sql = "select * from workspace"
                cursor.execute(
                    sql
                )
                col_names = [desc[0] for desc in cursor.description]
                results = cursor.fetchall()
                results_with_names = []
                for row in results:
                    data = dict(zip(col_names, row))
                    path = data["path"]
                    if not os.path.isdir(path):
                        data["name"] = data["name"] + "（不可用）"
                    results_with_names.append(data)
                self.write({"status": 1, "results": results_with_names})


class CreateWorkspace(tornado.web.RequestHandler):

    def set_default_headers(self):
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Headers', '*')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Content-type', 'application/json')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers',
                        'Content-Type, Access-Control-Allow-Origin, '
                        'Access-Control-Allow-Headers, '
                        'X-Requested-By, '
                        'Access-Control-Allow-Methods')

    def options(self):
        self.set_status(200)
        self.finish()

    async def post(self):
        data = json.loads(self.request.body)
        workspace_name = data['workspaceName']
        _id = generate()
        with pg_connection_pool.connection() as connection:
            with connection.cursor() as cursor:
                workspace_path = self.application.settings["app"]["data_path"] + _id
                if not os.path.isdir(workspace_path):
                    os.makedirs(workspace_path)
                sql = "INSERT INTO workspace (id, name,path) VALUES (%s, %s, %s)"
                insert_data = (_id, workspace_name, workspace_path)
                cursor.execute(sql, insert_data)
                connection.commit()
                self.write({"status": 1, "message": "workspace created successfully"})


class FindAnnoDatas(tornado.web.RequestHandler):

    def set_default_headers(self):
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Headers', '*')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Content-type', 'application/json')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers',
                        'Content-Type, Access-Control-Allow-Origin, '
                        'Access-Control-Allow-Headers, '
                        'X-Requested-By, '
                        'Access-Control-Allow-Methods')

    async def get(self):
        workspace_id = self.get_argument('workspaceId')
        with pg_connection_pool.connection() as connection:
            with connection.cursor() as cursor:
                sql = "select path from workspace where id = '{}'".format(workspace_id)
                cursor.execute(sql)
                results = cursor.fetchall()
                cursor.close()
                connection.close()
                workspace_path = results[0][0]
                if not os.path.isdir(workspace_path):
                    self.write({"status": 0, "message": "workspace not found", "results": []})
                    return
                files = os.listdir(workspace_path)
                col_names = ["name"]
                results = [dict(zip(col_names, (file,))) for file in files
                           if os.path.isfile(os.path.join(workspace_path, file))
                           and not file.endswith(".txt")]
                self.write({"status": 1, "results": results})


class SaveLabel(tornado.web.RequestHandler):
    def set_default_headers(self):
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Headers', '*')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Content-type', 'application/json')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers',
                        'Content-Type, Access-Control-Allow-Origin, '
                        'Access-Control-Allow-Headers, '
                        'X-Requested-By, '
                        'Access-Control-Allow-Methods')

    def options(self):
        self.set_status(200)
        self.finish()

    async def post(self):
        data = json.loads(self.request.body)
        workspace_id = data['workspaceId']
        name = data['name']
        color = data['color']
        _id = data["id"]
        opr = "update"
        if not _id:
            opr = "create"
            _id = generate()
        with pg_connection_pool.connection() as connection:
            with connection.cursor() as cursor:
                if opr == "create":
                    select_max_index_sql = "SELECT MAX(index) FROM label WHERE workspaceid='{}'".format(workspace_id)
                    cursor.execute(select_max_index_sql)
                    row = cursor.fetchone()
                    new_index = (row[0] + 1) if row and row[0] is not None else 1
                    sql = "INSERT INTO label (id,workspaceid,name,color,index) VALUES (%s,%s, %s,%s,%s)"
                    insert_data = (_id, workspace_id, name, color, new_index)
                    cursor.execute(sql, insert_data)
                if opr == "update":
                    sql = "UPDATE label SET name=%s,color=%s WHERE id = %s"
                    update_data = (name, color, _id)
                    cursor.execute(sql, update_data)
                connection.commit()
                self.write({"status": 1, "message": "label save or update successfully", "labelId": _id})


class FindLabels(tornado.web.RequestHandler):
    def set_default_headers(self):
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Headers', '*')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Content-type', 'application/json')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers',
                        'Content-Type, Access-Control-Allow-Origin, '
                        'Access-Control-Allow-Headers, '
                        'X-Requested-By, '
                        'Access-Control-Allow-Methods')

    async def get(self):
        workspace_id = self.get_argument("workspaceId")
        with pg_connection_pool.connection() as connection:
            with connection.cursor() as cursor:
                sql = "select * from label where workspaceid = '{}'".format(workspace_id)
                cursor.execute(sql)
                col_names = [desc[0] for desc in cursor.description]
                col_names = col_names + ["editStatus"]
                results = cursor.fetchall()
                results_extend = [row + (1,) for row in results]
                results_with_names = [dict(zip(col_names, row)) for row in results_extend]
                self.write({"status": 1, "results": results_with_names})


class DeleteLabels(tornado.web.RequestHandler):
    def set_default_headers(self):
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Headers', '*')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Content-type', 'application/json')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers',
                        'Content-Type, Access-Control-Allow-Origin, '
                        'Access-Control-Allow-Headers, '
                        'X-Requested-By, '
                        'Access-Control-Allow-Methods')

    def options(self):
        self.set_status(200)
        self.finish()

    async def post(self):
        data = json.loads(self.request.body)
        ids = str.split(data['ids'], ",")
        placeholders = ', '.join(['%s'] * len(ids))
        with pg_connection_pool.connection() as connection:
            with connection.cursor() as cursor:
                sql = f"delete from label where id in ({placeholders})"
                cursor.execute(sql, ids)
                connection.commit()
                self.write({"status": 1, "message": "label deleted successfully"})


class FindImage(tornado.web.RequestHandler):

    def set_default_headers(self):
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Headers', '*')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Content-type', 'application/json')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers',
                        'Content-Type, Access-Control-Allow-Origin, '
                        'Access-Control-Allow-Headers, '
                        'X-Requested-By, '
                        'Access-Control-Allow-Methods')

    def options(self):
        self.set_status(200)
        self.finish()

    async def get(self, workspaceId, fileName):
        with pg_connection_pool.connection() as connection:
            with connection.cursor() as cursor:
                sql = "select path from workspace where id = '{}'".format(workspaceId)
                cursor.execute(sql)
                results = cursor.fetchall()
                path = results[0][0]
                file = path + "/" + fileName
                if os.path.exists(file):
                    self.set_header('Content-Type', 'image/png')
                    with open(file, 'rb') as f:
                        self.write(f.read())
                else:
                    self.set_status(404)
                    self.write({"status": -1, "message": "file not found"})


class SaveLabelInstance(tornado.web.RequestHandler):
    def set_default_headers(self):
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Headers', '*')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Content-type', 'application/json')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers',
                        'Content-Type, Access-Control-Allow-Origin, '
                        'Access-Control-Allow-Headers, '
                        'X-Requested-By, '
                        'Access-Control-Allow-Methods')

    def options(self):
        self.set_status(200)
        self.finish()

    async def post(self):
        data = json.loads(self.request.body)
        workspace_id = data['workspaceId']
        file_name = data['filename']
        label = data['label']
        shape = data
        del shape["workspaceId"]
        del shape["filename"]
        del shape["label"]
        del shape["editStatus"]
        _id = data['id']
        opr = "update"
        if not _id:
            opr = "create"
            _id = generate()
        with pg_connection_pool.connection() as connection:
            with connection.cursor() as cursor:
                if opr == "create":
                    sql = "INSERT INTO labelinstance(id,workspaceid,filename,label,shape) VALUES(%s,%s,%s,%s,%s)"
                    insert_data = (_id, workspace_id, file_name, label, json.dumps(shape))
                    cursor.execute(sql, insert_data)
                    connection.commit()
                if opr == "update":
                    sql = "UPDATE labelinstance SET label = %s,shape=%s WHERE id = %s"
                    update_data = (label, json.dumps(shape), _id)
                    cursor.execute(sql, update_data)
                connection.commit()
                self.write({"status": 1, "message": "label instance save or update successfully", "instanceId": _id})


class FindLabelInstances(tornado.web.RequestHandler):
    def set_default_headers(self):
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Headers', '*')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Content-type', 'application/json')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers',
                        'Content-Type, Access-Control-Allow-Origin, '
                        'Access-Control-Allow-Headers, '
                        'X-Requested-By, '
                        'Access-Control-Allow-Methods')

    def get(self):
        workspace_id = self.get_argument("workspaceId")
        file_name = self.get_argument("filename")
        with pg_connection_pool.connection() as connection:
            with connection.cursor() as cursor:
                sql = "select * from labelinstance where workspaceid = '{}' and filename='{}'".format(workspace_id,
                                                                                                      file_name)
                cursor.execute(sql)
                datas = cursor.fetchall()
                col_names = [desc[0] for desc in cursor.description]
                data_with_names = [dict(zip(col_names, row)) for row in datas]
                results = []
                for row in data_with_names:
                    shape = json.loads(row["shape"])
                    shape["id"] = row["id"]
                    shape["label"] = row["label"]
                    shape["editStatus"] = 1
                    results.append(shape)

                self.write({"status": 1, "results": results, })


class DeleteLabelInstances(tornado.web.RequestHandler):
    def set_default_headers(self):
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Headers', '*')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Content-type', 'application/json')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers',
                        'Content-Type, Access-Control-Allow-Origin, '
                        'Access-Control-Allow-Headers, '
                        'X-Requested-By, '
                        'Access-Control-Allow-Methods')

    def options(self):
        self.set_status(200)
        self.finish()

    async def post(self):
        data = json.loads(self.request.body)
        ids = str.split(data['ids'], ",")
        placeholders = ', '.join(['%s'] * len(ids))
        with pg_connection_pool.connection() as connection:
            with connection.cursor() as cursor:
                sql = f"delete from labelinstance where id in ({placeholders})"
                cursor.execute(sql, ids)
                connection.commit()
                self.write({"status": 1, "message": "label instance deleted successfully"})


class CombineLabelInstances(tornado.web.RequestHandler):
    def set_default_headers(self):
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Headers', '*')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Content-type', 'application/json')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers',
                        'Content-Type, Access-Control-Allow-Origin, '
                        'Access-Control-Allow-Headers, '
                        'X-Requested-By, '
                        'Access-Control-Allow-Methods')

    def options(self):
        self.set_status(200)
        self.finish()

    async def post(self):
        data = json.loads(self.request.body)
        ids = str.split(data['ids'], ",")
        placeholders = ', '.join(['%s'] * len(ids))
        with pg_connection_pool.connection() as connection:
            with connection.cursor() as cursor:
                sql = f"select * from labelinstance where id in ({placeholders})"
                cursor.execute(sql, ids)
                datas = cursor.fetchall()
                col_names = [desc[0] for desc in cursor.description]
                datas_with_name = [dict(zip(col_names, row)) for row in datas]
                new_label_instance = {
                    "id": generate(),
                    "workspaceid": datas_with_name[0]["workspaceid"],
                    "filename": datas_with_name[0]["filename"],
                    "label": datas_with_name[0]["label"]
                }
                new_label_instance_shape = json.loads(datas_with_name[0]["shape"])
                if "x" in new_label_instance_shape:
                    del new_label_instance_shape["x"]
                if "y" in new_label_instance_shape:
                    del new_label_instance_shape["y"]
                polygons = []
                merged_yn = False
                for d in datas_with_name:
                    data_json = json.loads(d["shape"])
                    points = data_json["points"]
                    x_offset = data_json["x"] if "x" in data_json else 0
                    y_offset = data_json["y"] if "y" in data_json else 0
                    points_xy_pairs = [[points[i] + x_offset, points[i + 1] + y_offset]
                                       for i in range(0, len(points), 2)]
                    polygon = Polygon(points_xy_pairs)
                    if not polygon.is_valid:
                        polygon = polygon.buffer(0)
                        if not polygon.is_valid:
                            self.write({"status": 0, "message": "generate polygon by points error"})
                            return
                    polygons.append(polygon)
                merged = unary_union(polygons)
                if merged.geom_type == 'Polygon':
                    merged_yn = True
                if merged_yn:
                    merged_points_list = list(merged.exterior.coords)[:-1]
                    merged_points = [coord for point in merged_points_list for coord in point]
                    new_label_instance_shape["points"] = merged_points
                    new_label_instance["shape"] = json.dumps(new_label_instance_shape)
                    delete_id_placeholders = ",".join(['%s'] * len(ids))
                    delete_sql = f"delete from labelinstance where id in ({delete_id_placeholders})"
                    insert_sql = f"INSERT INTO labelinstance (id, workspaceid, filename, label, shape) VALUES (%s, %s, %s, %s, %s)"
                    insert_data = (new_label_instance["id"],
                                   new_label_instance["workspaceid"],
                                   new_label_instance["filename"],
                                   new_label_instance["label"],
                                   new_label_instance["shape"])
                    cursor.execute(delete_sql, ids)
                    cursor.execute(insert_sql, insert_data)
                    connection.commit()
                    self.write({"status": 1, "message": "形状合并成功"})
                else:
                    self.write({"status": 0, "message": "无法合并形状"})



class SamPredict(tornado.web.RequestHandler):
    def set_default_headers(self):
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Headers', '*')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Content-type', 'application/json')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers',
                        'Content-Type, Access-Control-Allow-Origin, '
                        'Access-Control-Allow-Headers, '
                        'X-Requested-By, '
                        'Access-Control-Allow-Methods')

    def options(self):
        self.set_status(200)
        self.finish()

    async def post(self):
        data = json.loads(self.request.body)
        with pg_connection_pool.connection() as connection:
            with connection.cursor() as cursor:
                sql = f"select path from workspace where id='{data['workspaceId']}'"
                cursor.execute(sql)
                results = cursor.fetchall()
                path = results[0][0]
                data["imagePath"] = path + "/" + data['imageName']
                connection.commit()
        data['imageFormat'] = "RGB"
        result = self.application.sam_predictor.predict(data)
        self.write({"status": 1, "result": result})


class FindLabelFiles(tornado.web.RequestHandler):
    def set_default_headers(self):
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Headers', '*')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Content-type', 'application/json')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers',
                        'Content-Type, Access-Control-Allow-Origin, '
                        'Access-Control-Allow-Headers, '
                        'X-Requested-By, '
                        'Access-Control-Allow-Methods')

    def options(self):
        self.set_status(200)
        self.finish()

    async def post(self):
        data = json.loads(self.request.body)
        workspace_id = data["workspaceid"]
        find_str = data["findStr"]
        with pg_connection_pool.connection() as connection:
            with connection.cursor() as cursor:
                sql = f"select path from workspace where id='{workspace_id}'"
                cursor.execute(sql)
                datas = cursor.fetchall()
                col_names = [desc[0] for desc in cursor.description]
                data_with_names = [dict(zip(col_names, row)) for row in datas]
                path = data_with_names[0]["path"]
                if not os.path.exists(path):
                    self.write({"status": 0, "message": "find workspace path error"})
                    return
                files = os.listdir(path)
                col_names = ["name"]
                result = [dict(zip(col_names, (file,)))
                          for file in files
                          if os.path.isfile(os.path.join(path, file))
                          and file.endswith(".txt") and file.find(find_str) != -1]
                self.write({"status": 1, "result": result})


class GenerateLabelFile(tornado.web.RequestHandler):
    def set_default_headers(self):
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Headers', '*')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Content-type', 'application/json')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers',
                        'Content-Type, Access-Control-Allow-Origin, '
                        'Access-Control-Allow-Headers, '
                        'X-Requested-By, '
                        'Access-Control-Allow-Methods')

    def options(self):
        self.set_status(200)
        self.finish()

    async def post(self):
        data = json.loads(self.request.body)
        workspace_id = data["workspaceid"]
        filename = data["filename"]
        imageConfigWidth = data["imageConfigWidth"]
        imageConfigHeight = data["imageConfigHeight"]
        with pg_connection_pool.connection() as connection:
            with connection.cursor() as cursor:
                select_path_sql = "select path from workspace where id='{}'".format(workspace_id)
                cursor.execute(select_path_sql)
                workspace_datas = cursor.fetchall()
                workspace_col_names = [desc[0] for desc in cursor.description]
                workspace_data_with_names = [dict(zip(workspace_col_names, row)) for row in workspace_datas]
                path = workspace_data_with_names[0]["path"]
                if not os.path.exists(path):
                    self.write({"status": 0, "message": "找不到工作区路径"})
                    return

                select_label_name_sql = "select * from label where workspaceid='{}'".format(workspace_id)
                cursor.execute(select_label_name_sql)
                labels = cursor.fetchall()
                label_col_names = [desc[0] for desc in cursor.description]
                labels_with_names = [dict(zip(label_col_names, row)) for row in labels]
                labels_with_index = {}
                for labels_with_names in labels_with_names:
                    labels_with_index[labels_with_names["name"]] = labels_with_names["index"]

                image_file = path + "/" + filename
                image_cv2 = cv2.imread(image_file)
                image_cv2 = cv2.cvtColor(image_cv2, cv2.COLOR_BGR2RGB)
                ori_image_height, ori_image_width, _ = image_cv2.shape
                ratio_height = ori_image_height / imageConfigHeight
                ratio_width = ori_image_width / imageConfigWidth
                select_shape_sql = "select * from labelinstance where workspaceid='{}' and filename='{}'".format(
                    workspace_id, filename)
                cursor.execute(select_shape_sql)
                labelinstance_datas = cursor.fetchall()
                labelinstance_col_names = [desc[0] for desc in cursor.description]
                labelinstance_data_with_names = [dict(zip(labelinstance_col_names, row)) for row in labelinstance_datas]
                label_lines = []
                for labelinstance in labelinstance_data_with_names:
                    label_line = []
                    label = labelinstance["label"]
                    index_of_label = labels_with_index[label]
                    label_line.append("{}".format(index_of_label))
                    shape = json.loads(labelinstance["shape"])
                    points = shape["points"]
                    x_offset = shape["x"] if "x" in shape else 0
                    y_offset = shape["y"] if "y" in shape else 0
                    x_image = float(shape["imagex"])
                    y_image = float(shape["imagey"])
                    points_normalized = [[(points[i] - x_image + x_offset) * ratio_width / imageConfigWidth,
                                          (points[i + 1] - y_image + y_offset) * ratio_height / imageConfigHeight]
                                         for i in range(0, len(points), 2)]
                    for x, y in points_normalized:
                        label_line.extend(["{:.4f}".format(x), "{:.4f}".format(y)])

                    label_lines.append(" ".join(label_line))
                label_file_path = path + "/" + os.path.splitext(filename)[0] + ".txt"
                with open(label_file_path, "w", encoding='utf-8') as f:
                    f.write("\n".join(label_lines))
                    self.write({"status": 1, "message": "生成标签文件成功"})
